defmodule CheckValid do
	@moduledoc false

	import Util
	require Logger
	import ErrCode, only: [input_error: 0]

	# 把不属于该对象的属性踢出来, 不包括关系对象及关系, 用 Pango.CypherRepo, 的增改的时候用
	@doc """
	把不属于 mod 对象的字段剔除出去
	{属于mod对象的字段, 不属于mod对象的字段}
	"""
	@spec valid_pop!(map, atom) :: {map, map}
	def valid_pop!(args, mod) do
		fields = mod.fields()
		Enum.reduce(
			args,
			{args, %{}},
			fn ({k, v}, {acc, acc2}) ->
				case Map.get(fields, k) do
					%{type: type} ->
						v = type_valid?(type, k, v)
						{Map.put(acc, k, v), acc2}
					nil ->
						{Map.delete(acc, k), Map.put(acc2, k, v)}
				end
			end
		)
	end

	def pop(args, mod) do
		fields = mod.fields()
		Enum.reduce(
			args,
			{args, %{}},
			fn ({k, v}, {acc, acc2}) ->
				case Map.get(fields, k) do
					%{relation_object: _} ->
						{Map.delete(acc, k), Map.put(acc2, k, v)}
					%{mod: _} ->
						{Map.delete(acc, k), Map.put(acc2, k, v)}
					%{type: type} ->
						{Map.put(acc, k, v), acc2}
					nil ->
						{Map.delete(acc, k), Map.put(acc2, k, v)}
				end
			end
		)
	end

	# 剔除关系对象和关系, 以及不属于mod的属性, 自己写增删改的语句时候用,
	# 不用再做剔除 不属于这个对象的字段和关系了
	def valid_pop_rel!(args, mod) do
		fields = mod.fields()
		Enum.reduce(
			args,
			{args, %{}},
			fn ({k, v}, {acc, acc2}) ->
				case Map.get(fields, k) do
					%{relation_object: _} ->
						{Map.delete(acc, k), Map.put(acc2, k, v)}
					%{mod: _} ->
						{Map.delete(acc, k), Map.put(acc2, k, v)}
					%{type: type} ->
						v = type_valid?(type, k, v)
						{Map.put(acc, k, v), acc2}
					nil ->
						{Map.delete(acc, k), Map.put(acc2, k, v)}
				end
			end
		)
	end

	# 只检查参数
	@doc """
	把不属于 mod 对象的字段剔除出去
	return: 属于mod对象的字段
	"""
	@spec valid!(map, atom) :: map
	def valid!(args, mod) do
		fields = mod.fields()
		Enum.reduce(
			args,
			args,
			fn ({k, v}, acc) ->
				case Map.get(fields, k) do
					%{type: type} ->
						v = type_valid?(type, k, v)
						Map.put(acc, k, v)
					nil ->
						Map.delete(acc, k)
				end
			end
		)
	end


	def type_valid?(type, k, v1) when type in [:integer, :int8] do
		v = to_integer(v1)
		is_integer(v) || PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v1)} input_error")
		v
	end
	def type_valid?(:string, k, v) do
		is_binary(v) || PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v)} input_error")
		v
	end
	def type_valid?(:json, k, v) do
		is_binary(v) || PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v)} input_error")
		v
	end
	def type_valid?(:jsonb, k, v) do
		is_map(v) || is_list(v) || PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v)} input_error")
		v
	end
	def type_valid?(:float, k, v1) do
		v = to_float(v1)
		is_float(v) || PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v1)} input_error")
		v
	end
	def type_valid?({:array, :string}, k, v1) do
		v = binary_to_list(v1)
		(is_list(v) && is_list(v)) ||
			PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v1)} input_error")
		v
	end
	def type_valid?({:array, :integer}, k, v1) do
		v = binary_to_list(v1)
		(is_list(v) && Enum.all?(v, &(is_integer(&1)))) ||
			PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v1)} input_error")
		v
	end
	def type_valid?(:boolean, k, v1) do
		v = to_bool(v1)
		is_boolean(v) || PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v1)} input_error")
		v
	end
	def type_valid?(:date, k, v1) do
		NaiveDateTime.from_iso8601(v1)
		|> case do
				 {:ok, v} ->
					 v
				 _ ->
					 PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v1)} input_error")
			 end
	end
	def type_valid?(:ymd, k, v1) do
		Date.from_iso8601(v1)
		|> case do
				 {:ok, v} ->
					 v
				 _ ->
					 PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v1)} input_error")
			 end
	end
	def type_valid?(:one_to_one, k, v) do
		is_binary(v) || is_integer(v) || PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v)} input_error")
		v
	end
	def type_valid?(:one_to_many, k, v) do
		is_list(v) || PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v)} input_error")
		v
	end
	def type_valid?(_, k, v) do
		PrivateCandy.debug_remind(__ENV__, "#{inspect(k)}, #{inspect(v)} input_error")
	end

end
